/**
 * File: Utils.js
 * Description:
 * License:
 * 
 * @author Andrea Ballatore
 */

/**
 * global flag to enable/disable assertions in GeoAdapta
 */
_b_USE_ASSERTIONS = true;
_url_HOME_PAGE  		= "/GeoAdaptaApp/";
_url_REGISTRATION_PAGE 	= "/GeoAdaptaApp/overview/registration";
_url_LOGIN_PAGE 		= "/GeoAdaptaApp/overview/login";


/**
 * simple assertion mechanism to check code integrity
 * @param bCondition a condition that must be TRUE
 * @param sErrorMessage error thrown if bCondition is FALSE
 * @return void
 * @throws Error
 */
function assert(bCondition, sErrorMessage) {
	if (!_b_USE_ASSERTIONS) return;
	
	if (!sErrorMessage)
		throw new Error("Assertion: \""+bCondition+"\", missing description.");
	
	if (!sErrorMessage) {
		sErrorMessage = bCondition + ' - no description available';
	}
	if (!bCondition) {
		if (log)
			log.error("Assertion failed:",sErrorMessage);
		throw new Error("Assertion failed: "+sErrorMessage);
	}
}

/**
 * Check if a Javascript object exists
 * 
 * @param obj any Javascript object
 * @return boolean
 */
function _exists(obj) {
	return typeof obj != 'undefined';
}

/**
 * used to set the right 'this' in a function
 * @param context the right 'this'
 * @param fn function with wrong 'this'
 * @return function with right 'this'
 */
function _getFunctionInContext( context, fn ) {
	assert(context,'_getFunctionInContext: null context');
	assert(fn,'_getFunctionInContext: null function');
	assert(typeof fn.apply == 'function','_getFunctionInContext: null fn.apply'); 
	
	return function() {
		return fn.apply( context, arguments );
	};
}

/**
 * 
 * @return JSON-compatible string contaning info about Web Browser
 */
function _getBrowserInfo(){
	assert(navigator,'navigator is null');
	var result = navigator.userAgent 
		+ ' ' + navigator.appName;
	return _cleanStringForJSON( result );
}

/**
 * 
 * @return JSON-compatible string contaning info about Browser's OS
 */
function _getOSInfo(){
	assert(navigator,'navigator is null');
	var result = navigator.platform 
		+ ' ' + navigator.appVersion;
	return _cleanStringForJSON( result );
}

/**
 * display and log Browser not supported error
 * @param msg
 * @return void
 */
function _alertBrowserNotSupported( msg ){
	//alert("Browser Not Supported!");
	log.error("Browser Not Supported! - " + msg );
}

/**
 * shows a typical AJAX spinner
 * @return void
 */
function _showSpinner(){
	assert( $('spinner'), '_showSpinner: spinner div undefined' );
	$('spinner').show();
}

/**
 * hides a typical AJAX spinner
 * @return void
 */
function _hideSpinner(){
	assert( $('spinner'), "_hideSpinner: null div" );
	$('spinner').hide();
}

/**
 * output red text into div console
 * @param console div where to inject msg
 * @param msg text
 * @return void
 */
function _consolePrintError( console, msg ){
	assert( console, "_consolePrintError: null console" );
	console.setStyle({ color: '#a00' });
	console.update( msg );	
}

/**
 * output green text into div console
 * @param console div where to inject msg
 * @param msg text
 * @return void
 */
function _consolePrintInfo( console, msg ){
	assert( console, "_consolePrintInfo: null console" );
	console.setStyle({ color: '#0a0' });
	console.update( msg );
}

/**
 * cleans a string removing special characters that might conflict with JSON
 * @param str some text full of special characters
 * @return clean and safe string for JSON
 */
function _cleanStringForJSON( str ){
	allowedChar = "_";
	
	cleanStr = str	   .replace(/\[/g,allowedChar);
	cleanStr = cleanStr.replace(/\]/g,allowedChar);
	cleanStr = cleanStr.replace(/\;/g,allowedChar);
	cleanStr = cleanStr.replace(/\:/g,allowedChar);
	cleanStr = cleanStr.replace(/\{/g,allowedChar);
	cleanStr = cleanStr.replace(/\}/g,allowedChar);
	cleanStr = cleanStr.replace(/\(/g,allowedChar);
	cleanStr = cleanStr.replace(/\)/g,allowedChar);
	cleanStr = cleanStr.replace(/\//g,allowedChar);
	
	//log.warn( str );
	//log.warn ( cleanStr );
	return cleanStr;
}

/**
 * populate select control with values contained in the responseText from an AJAX call
 * specified in JSON
 * e.g. responseText: [1,2,3,4,5]
 * 
 * @param e ajax call return value
 * @param selectId target select control
 * @return void
 */
function _updateSelect(e, selectId) {
	log.debug('_updateSelect');
	log.warn(e.responseText)
	// The response comes back as a bunch-o-JSON
	var elems = eval("(" + e.responseText + ")");	// evaluate JSON
	log.debug('_updateSelect: loaded '+elems.size() + ' elements from response');
	
	_clearSelect( selectId )
	_addDefaultEntryToSelect( selectId, "-Select-")
	
	if (elems) {
		var rselect = $(selectId)

		// Rebuild the select
		for (var i=0; i < elems.length; i++) {
			var elem = elems[i];
			var opt = document.createElement('option');
			
			assert(elem, '_updateSelect: elem id null');
			
			opt.text = elem;
			opt.value = elem;
			
			_addOptionToSelect(selectId, opt)
		}
	}
}

/**
 * clear select control
 * @param selectId target select control
 * @return void
 */
function _clearSelect( selectId ){
	assert( selectId );
	var rselect = $(selectId);

	// Clear all previous options
	var l = rselect.length;

	while (l > 0) {
		l--;
		rselect.remove(l);
	}
}

/**
 * Add default entry to select control with msg as a text
 * @param selectId id of the select control
 * @param msg message (e.g. "please select something")
 * @return void
 */
function _addDefaultEntryToSelect( selectId, msg ){
	assert( selectId );
	var rselect = $(selectId);
	var opt = document.createElement('option');
	opt.text = msg;
	opt.value = -1;
	_addOptionToSelect( selectId, opt );
}

/**
 * Add option to a select control and log error if it fails
 * @param selectId id of select control
 * @param option standard option
 * @return void
 */
function _addOptionToSelect( selectId, option ){
	var rselect = $(selectId);
	assert(rselect, '_addOptionToSelect: ' + selectId + ' doesn\'t exist');
  	try {
    	rselect.add(option,null) // standards compliant; doesn't work in IE
  	} catch( ex ) {
  		try {
  			rselect.add(option)
  		} catch (e) {
  			log.error('_addOptionToSelect:')
  			log.error(ex)
  			log.error(e)
		}
	}
}

/**
 * 
 * @return true if the web browser is Internet Explorer  
 */
function _isMicrosoftInternetExplorer(){
	var info = _getBrowserInfo();
	var patt1=/explorer/gi;
	var match = info.match(patt1);
	if (match && match.size() > 0)
		return true;
	else return false;
}

/**
 * init log4javascript and set the object 'log' for Desktop pages.
 * after calling this function the object log will be available for
 * logging anything from javascript.
 * 
 * @return log4javascript object or null if something breaks
 */
function _initLoggerDesktop(){
	
	try{
		//var loggerDiv = document.getElementById( loggerDivId );
		//if (!loggerDiv){
		//	throw new Error("loggerDiv null");
		//}
		var log = null;
		
		if ( _isMicrosoftInternetExplorer() ){
			// use popup console
			log = log4javascript.getDefaultLogger();
		} else {
			// use nicer console
			log = log4javascript.getLogger();
			//log4j_appender = new log4javascript.InPageAppender( loggerDiv );
			log4j_appender = new log4javascript.BrowserConsoleAppender();
			assert(log4j_appender,"_initLoggerDesktop: log4j_appender null");
			log.addAppender( log4j_appender );
		}
		assert(log,"_initLoggerDesktop: log null");
		// test logger
		log.debug("logger started");
		return log;
	} catch (e) {
		errorMsg = '_initLoggerDesktop: Failed to init logger. '+e;
		alert( errorMsg );
		return null;
	}
}

/**
 * init log4javascript with an AJAX appender and 
 * set the object 'log' for Mobile pages.
 * 
 * @return log4javascript object or null if something breaks
 */
function _initLoggerMobile(){
	var log = log4javascript.getLogger();
	assert(log,"_initLoggerMobile: null log");
	// send log to server, don't use popup window
	var ajaxAppender = new log4javascript.AjaxAppender( "/GeoAdaptaApp/geoLogger/logClientMsg" );
	log.addAppender(ajaxAppender);
	assert(log,'_initLoggerMobile: log is null');
	log.debug("logger started");
	return log;
}

/**
 * matches the words "login" and "password" in code
 * @param code input text
 * @return boolean
 */
function _isHtmlLoginPage( code ){
	if (!code) return false;
	var bFound = false;
	
	var patt1=/login/gi;
	var match = code.match(patt1);
	if (match && match.size() > 0)
		bFound = true;
	
	if (bFound){
		patt1=/password/gi;
		match = code.match(patt1);
		if (match && match.size() > 0)
			bFound = true;
		else bFound = false;
	}
	return bFound;
}

/**
 * matches the words "error" and "exception" in code
 * @param code input text
 * @return boolean
 */
function _isHtmlErrorPage( code ){
	if (!code) return false;
	var bFound = false;
	
	var patt1=/error/gi;
	var match = code.match(patt1);
	if (match && match.size() > 0)
		bFound = true;
	
	if (bFound){
		patt1=/exception/gi;
		match = code.match(patt1);
		if (match && match.size() > 0)
			bFound = true;
		else bFound = false;
	}
	return bFound;
}

/**
 * Check validity of lat lon
 * @param lat
 * @param lon
 * @return boolean
 */
function _validateLatLon( lat, lon ){
	var bValid = 		lon >= -180 && lon <= 180;
	bValid = bValid &&  lat >= -90  && lat <= 90;
	if (!bValid){
		log.warn("_validateLatLon: Invalid latlon: "+lat+" "+lon);
	}
	return bValid;
};


/**
 * 
 * @return current host, e.g. itc.ktu.com
 */
function _getCurrentPageHost(){
	var url = window.location.host;
	assert( url, "_getCurrentPageHost: null host" );
	return url;
}

/**
 * 
 * @return current path name, e.g. /javascript/page/test.html
 */
function _getCurrentPagePath(){
	var url = window.location.pathname;
	assert( url, "_getCurrentPagePath: null pathname" );
	return url;
};

/**
 * show a html page overlay rep
 * 
 * @param contextId a valid context id
 * @return void
 */
function _showPageOverlay( contextId ){
	url = "/GeoAdaptaApp/geoLogger/getPageOverlay";
	paramsStr = 'contextId=' + contextId;
	//log.debug('sendQueueToServer '+paramsStr);
	//alert(paramsStr);
	
    new Ajax.Request(url, {
       parameters: paramsStr,
	   method: 'post',
	   onSuccess: _showPageOverlaySuccess(),
   	   onFailure: _showPageOverlayFailure()
    });
};

/**
 * Ask for a valid context Id and then make an ajax call to get the relevant overlay
 * 
 * @return void
 */
function _askForContextIdForPageOverlay(){
	Ext.Msg.prompt('Display context as page overlay', 'Please enter a valid context Id:', function(btn, text){
	    if (btn == 'ok'){
	    	text = text.trim();
	    	if (!text) return;
	    	_showPageOverlay( text );
	    }
	});
}

/**
 * Ask for a valid context Id and then make an ajax call to get the relevant overlay (iPhone, no Ext)
 * 
 * @return void
 */
function _askForContextIdForPageOverlayMobile(){
	var text = prompt("Display context as page overlay.\nPlease enter a valid Context Id");
	if (text!=null && text!="")
	{
    	text = text.trim();
    	if (!text) return;
    	_showPageOverlay( text );
	}
}

/**
 * @see _showPageOverlay
 * @return void
 */
function _showPageOverlaySuccess(){
	return function(transport) {
		var targetDiv = $('pageVisualiser');
		targetDiv.innerHTML = '';
		
		if (!transport.responseText){
			log.error('_showPageOverlaySuccess: responseText is null');
			return;
		}
		assert(targetDiv, '_showPageOverlaySuccess: targetDiv is null');
		targetDiv.innerHTML = transport.responseText;  
	};
};

/**
 * @see _showPageOverlay
 * @return void
 */
function _showPageOverlayFailure(){
	return function(transport) {
		var targetDiv = $('pageVisualiser');
		targetDiv.innerHTML = '';
		
		var retValue = 'getPageOverlay failed, can\'t show context';
		log.error(retValue);
		
		if (transport.responseText){
			retValue = transport.responseText;
		}
	};	
};

/**
 * Returns true if strToFind is present in targetText (case insensitive)
 * 
 * @param strToFind
 * @param targetText
 * @return boolean
 */
function _matchString( strToFind, targetText ){
	if (!strToFind || !targetText) return false;
	
	var patt1= new RegExp( strToFind,"gi" );
	var match = targetText.match(patt1);
	if (match && match.size() > 0)
		 return true;
	else return false;
};

/**
 * String Trim. 
 * Workaround for browsers that don't support it like Safari Mobile.
 */
String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, '');
};

/**
 * 
 * @return array containing the page scroll [x,y]
 */
function _getPageScrollXY() {
	var scrOfX = 0, scrOfY = 0;
	if( typeof( window.pageYOffset ) == 'number' ) {
	//Netscape compliant
		scrOfY = window.pageYOffset;
		scrOfX = window.pageXOffset;
	} else if( document.body && ( document.body.scrollLeft || document.body.scrollTop ) ) {
		//DOM compliant
		scrOfY = document.body.scrollTop;
		scrOfX = document.body.scrollLeft;
	} else if( document.documentElement && ( document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
		//IE6 standards compliant mode
		scrOfY = document.documentElement.scrollTop;
		scrOfX = document.documentElement.scrollLeft;
	}
	assert(typeof( scrOfX ) == 'number','_getPageScrollXY: x null');
	assert(typeof( scrOfY ) == 'number','_getPageScrollXY: y null');
	return [ scrOfX, scrOfY ];
}

/**
 * 
 * @return array with the current page size in pixels [width, height]
 */
function _getPageSizeXY(){
	if( typeof( window.innerWidth ) == 'number' ) {
		//Netscape compliant
			scrOfY = window.innerHeight;
			scrOfX = window.innerWidth;
	} else if( document.body && document.body.offsetWidth ) {
			//DOM compliant
			scrOfY = document.body.offsetHeight;
			scrOfX = document.body.offsetWidth;
	}
	assert(typeof( scrOfX ) == 'number','_getPageSizeXY: x null');
	assert(typeof( scrOfY ) == 'number','_getPageSizeXY: y null');
	return [ scrOfX, scrOfY ];
}

/**
 * 
 * @param obj any object
 * @return true if it's a number
 */
function _isNumber( obj ){
	return typeof( obj ) == 'number';
}

/**
 * redirect to login page, unless already on login page
 * @return void
 */
function _redirectToLogin(){
	if (_getCurrentPagePath() != _url_LOGIN_PAGE
	 && _getCurrentPagePath() != _url_HOME_PAGE
	 && _getCurrentPagePath() != _url_REGISTRATION_PAGE){
		alert("Your session timed out. Please log in again.");
		window.location = _url_LOGIN_PAGE;
	}
}

/**
 * 
 * @param pointOne a 2d point
 * @param pointTwo a 2d point
 * @return distance
 */
function _getDistanceBetween2Points( pointOne, pointTwo ){
	assert(pointOne,"_getDistanceBetween2Points: pointOne null");
	assert(pointTwo,"_getDistanceBetween2Points: pointTwo null");
	
	if (pointTwo.x == pointOne.x && pointTwo.y == pointOne.y){
		return 0;
	}
	
	var tmp = Math.pow( (pointTwo.x - pointOne.x) ,2)
	        + Math.pow( (pointTwo.y - pointOne.y) ,2);
	
	assert(tmp >= 0,"_getDistanceBetween2Points: tmp < 0: "+tmp);
	assert(_isNumber(tmp),"_getDistanceBetween2Points: tmp is not a number");
	
	var d = Math.sqrt( tmp );
	assert( d >= 0, "_getDistanceBetween2Points: distance < 0: " + d);
	
	return d;
}

/**
 * Show overlay on the whole page
 * 
 * @param msg
 * @return null
 */
function _lockPage( msg ){
	Ext.getBody().mask( msg );
}

/**
 * Clear page from overlay
 * 
 * @param msg
 * @return null
 */
function _unlockPage(){
	Ext.getBody().unmask();
}

/**
 * 
 * @param msg
 * @return
 */
function _showPopupServerError( msg ){
	log.warn("_showPopupServerError: " + msg);
	Ext.MessageBox.show({
        title: 'Server error',
        msg: msg,
        buttons: Ext.MessageBox.OK
     });
}